# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
import os

from conan import ConanFile
from conan.tools.scm import Git
from conan.tools.files import copy, chdir
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout

required_conan_version = ">=2.13.0"


class MetryConan(ConanFile):
    name = "opentelemetry"
    license = "Mulan PSL v2"
    settings = "os", "arch", "compiler", "build_type"
    exports_sources = ["permissions.ini", "build/*", "patches/**"]
    options = {
        "shared": [True, False],
        "with_abseil": [True, False],
        "with_otlp": [True, False],
        "with_otlp_grpc": [True, False],
        "with_zipkin": [True, False],
        "with_prometheus": [True, False],
        "with_elasticsearch": [True, False]
    }
    default_options = {
        "shared": True,
        "with_abseil": True,
        "with_otlp": False,
        "with_otlp_grpc": True,
        "with_zipkin": False,
        "with_prometheus": False,
        "with_elasticsearch": False
    }
    _cmake = None

    def layout(self):
        cmake_layout(self)

    def requirements(self):
        requires = self.conan_data.get("requires", {}).get(self.version, [])
        for req in requires:
            self.requires(req)

    def build_requirements(self):
        if os.path.exists('/home/bin/protoc') and os.path.exists('/home/bin/grpc_cpp_plugin'):
            return
        requires = self.conan_data.get("tool_requires", {}).get(self.version, [])
        for req in requires:
            self.tool_requires(req)

    def source(self):
        source = self.conan_data["sources"][self.version]
        git = Git(self)
        git.fetch_commit(url=source["url"], commit=source["branch"])
    
    def build(self):
        with chdir(self, self.source_folder):
            self.run("git apply patches/0001-3.patch")
            if not self.settings.arch == "x86_64":
                self.run("sed -i 's/add_subdirectory(ostream)//' exporters/CMakeLists.txt")
                self.run("sed -i 's/add_subdirectory(memory)//' exporters/CMakeLists.txt")

        cmake = self._configure_cmake()
        cmake.build()

    def generate(self):
        tc = CMakeToolchain(self)
        if self.settings.arch == "armv8":
            os.environ['ARCH'] = "arm64"
        if self.settings.arch == "x86_64":
            os.environ['ARCH'] = "x86"
        tc.variables["BUILD_TESTING"] = "OFF"
        tc.variables["BUILD_SHARED_LIBS"] = "ON"
        tc.variables["WITH_EXAMPLES"] = "OFF"
        tc.variables["WITH_ABSEIL"] = self.options.with_abseil
        tc.variables["WITH_OTLP_GRPC"] = self.options.with_otlp_grpc
        tc.variables["OTELCPP_PROTO_PATH"] = self.dependencies["opentelemetry-proto"].package_folder
        tc.variables["WITH_ZIPKIN"] = self.options.with_zipkin
        tc.variables["WITH_PROMETHEUS"] = self.options.with_prometheus
        tc.variables["WITH_ELASTICSEARCH"] = self.options.with_elasticsearch
        tc.variables["CMAKE_CXX_STANDARD"] = 17
        tc.variables["CMAKE_FIND_LIBRARY_SUFFIXES"] = ".a"
        tc.variables["WITH_ABI_VERSION_1"] = "OFF"
        tc.variables["WITH_ABI_VERSION_2"] = "ON"
        if self.options.with_otlp_grpc:
            self._configure_grpc_cmake(tc)
        if self.settings.arch == "armv8" or self.settings.arch == "x86_64":
            tc.variables["CMAKE_INSTALL_LIBDIR"] = "usr/lib64"
        else:
            tc.variables["CMAKE_INSTALL_LIBDIR"] = "usr/lib"
        tc.extra_cflags = ["-D_FORTIFY_SOURCE=2 -O2"]
        tc.extra_cxxflags = ["-D_FORTIFY_SOURCE=2 -O2"]
        tc.generate()

    def package(self):
        cmake = self._configure_cmake()
        cmake.install()
        copy(self, "permissions.ini", src=self.export_sources_folder, dst=self.package_folder)
        copy(self, "*", src=os.path.join(self.export_sources_folder, "build"), 
             dst=os.path.join(self.package_folder, "include"))

    def package_info(self):
        # 核心组件
        self.cpp_info.components["core"].libs = ["opentelemetry_common"]
        self.cpp_info.components["core"].libdirs = ["usr/lib64"]
        # 追踪模块
        self.cpp_info.components["trace"].libs = ["opentelemetry_trace"]
        self.cpp_info.components["trace"].requires = ["core"]
        self.cpp_info.components["trace"].libdirs = ["usr/lib64"]
        # 度量模块
        self.cpp_info.components["metrics"].libs = ["opentelemetry_metrics"]
        self.cpp_info.components["metrics"].requires = ["core"]
        self.cpp_info.components["metrics"].libdirs = ["usr/lib64"]
        # 日志模块
        self.cpp_info.components["logs"].libs = ["opentelemetry_logs"]
        self.cpp_info.components["logs"].requires = ["core"]
        self.cpp_info.components["logs"].libdirs = ["usr/lib64"]

        # OLTP导出器（依赖protobuf）
        if self.options.with_otlp:
            self.cpp_info.components["otlp"].libs = ["opentelemetry_exporter_otlp"]
            self.cpp_info.components["otlp"].libdirs = ["usr/lib64"]

        if self.options.with_otlp_grpc:
            self.cpp_info.components["otlp_grpc"].libs = [
                "opentelemetry_exporter_otlp_grpc",
                "opentelemetry_exporter_otlp_grpc_log", 
                "opentelemetry_exporter_otlp_grpc_metrics"
            ]
            self.cpp_info.components["otlp_grpc"].libdirs = ["usr/lib64"]
        if self.settings.arch == "x86_64":
            self.cpp_info.components["exporter_ostream"].libs = [
                "opentelemetry_exporter_ostream_metrics",
                "opentelemetry_exporter_ostream_logs",
                "opentelemetry_exporter_ostream_span"
            ]
            self.cpp_info.components["exporter_ostream"].libdirs = ["usr/lib64"]

    def _configure_grpc_cmake(self, tc):
        if os.path.exists('/home/bin/protoc') and os.path.exists('/home/bin/grpc_cpp_plugin'):
            lib_path = '/home/lib'
            os.environ['LD_LIBRARY_PATH'] = f'{lib_path}:{os.environ.get('LD_LIBRARY_PATH', '')}'
            tc.variables['Protobuf_PROTOC_EXECUTABLE'] = '/home/bin/protoc'
            tc.variables['CMAKE_PROGRAM_PATH'] = '/home/bin/'
        else:
            lib_path = os.path.join(self.dependencies.build['protobuf'].package_folder, 'lib')
            os.environ['LD_LIBRARY_PATH'] = f'{lib_path}:{os.environ.get('LD_LIBRARY_PATH', '')}'
            protoc_path = os.path.join(self.dependencies.build['protobuf'].package_folder, 'bin/protoc')
            tc.variables['Protobuf_PROTOC_EXECUTABLE'] = protoc_path
        protobuf_rootpath = self.dependencies["protobuf"].package_folder
        protobuf_dir = os.path.join(protobuf_rootpath, "usr", "lib64")
        grpc_rootpath = self.dependencies["grpc"].package_folder
        grpc_dir = os.path.join(grpc_rootpath, "lib", "cmake", "grpc")
        re2_rootpath = self.dependencies["re2"].package_folder
        re2_dir = os.path.join(re2_rootpath, "lib", "cmake", "re2")
        # Add paths to CMake definitions
        cares_rootpath = self.dependencies["c_ares"].package_folder
        absl_rootpath = self.dependencies["abseil"].package_folder
        absl_dir = os.path.join(absl_rootpath, "lib", "cmake", "absl")
        tc.variables["CMAKE_PREFIX_PATH"] = (
            f"{protobuf_dir};{re2_dir};{cares_rootpath};{absl_dir};"
        )
        tc.variables["CMAKE_PREFIX_PATH"] += f"{grpc_dir};{protobuf_rootpath}"

    def _configure_cmake(self):
        if self._cmake:
            return self._cmake
        self._cmake = CMake(self)
        self._cmake.configure(cli_args=["--no-warn-unused-cli"])
        return self._cmake